//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
/******************************************************************************
**
** INTEL CONFIDENTIAL
** Copyright 2003-2005 Intel Corporation All Rights Reserved.
**
** The source code contained or described herein and all documents
** related to the source code (Material) are owned by Intel Corporation
** or its suppliers or licensors.  Title to the Material remains with
** Intel Corporation or its suppliers and licensors. The Material contains
** trade secrets and proprietary and confidential information of Intel
** or its suppliers and licensors. The Material is protected by worldwide
** copyright and trade secret laws and treaty provisions. No part of the
** Material may be used, copied, reproduced, modified, published, uploaded,
** posted, transmitted, distributed, or disclosed in any way without Intel's
** prior express written permission.
**
** No license under any patent, copyright, trade secret or other intellectual
** property right is granted to or conferred upon you by disclosure or
** delivery of the Materials, either expressly, by implication, inducement,
** estoppel or otherwise. Any license under such intellectual property rights
** must be express and approved by Intel in writing.
**
**
******************************************************************************/

/****************************************************************
*    SDMMC.c
*       This is the SDMMC driver that is used with eboot
*       to load images off of SD/MMC cards.
*
*    Revision History:
*       Jason White - 1/4/06 - initial draft
*       Jason White - 1/11/06 - added card initialization
*       Jason White - 1/16/06 - modularized code into functions
*       Jason White - 1/18/06 - got readBlock to work 
*		Jason White - 2/15/06 - added SD card functionality
****************************************************************/

#ifndef NO_NH040526A_LOGGING
#ifndef ENABLE_DEBUG_MESSAGES
#define ENABLE_DEBUG_MESSAGES 1
#endif

#ifndef ENABLE_LOG_MESSAGES
#define ENABLE_LOG_MESSAGES 1
#endif

#if ENABLE_DEBUG_MESSAGES
#define DBGMSG(cond, pargs) NKDbgPrintfW pargs
#else
#define DBGMSG(cond, pargs) 0
#endif
#if ENABLE_LOG_MESSAGES
#define LOGMSG(cond, pargs) NKDbgPrintfW pargs 
#else
#define LOGMSG(cond, pargs) 0
#endif

#define LOGGING_ENABLED (ENABLE_DEBUG_MESSAGES || ENABLE_LOG_MESSAGES)
#if LOGGING_ENABLED
#include <windows.h>   // for NKDbgPrintfW()
#endif
#endif /* NH040526A_LOGGING */

#include "SDMMC.h"

/***********************************************************
*    SDMMCInit()
*       Initializes the SDMMC port on the platform and all
*       of the other necessary hardware needed for SDMMC
*       reading.
*    input:
*       none
*    output:
*       All the needed hardware should be initialized.
*    returns:
*       the RCA of the card
************************************************************/
XLLP_UINT32_T SDMMCInit(P_XLLP_MMC_CONTEXT_T pContext, P_XLLP_CLKMGR_T clkmgr)
{
    XLLP_STATUS_T status;
    int tries;

    //need to make sure the card is initialized
    for (tries = 0; tries < 2; tries++)
    {
        DBGMSG(1, (TEXT("Enabling the clock for MMC...\r\n")));
        if (!XllpClockEnable(clkmgr, XLLP_CLK_MMC0, XLLP_TRUE))
            DBGMSG(1, (TEXT("The clock for MMC has been successfully enabled!\r\n")));
        else
            DBGMSG(1, (TEXT("The clock for MMC was already enabled!\r\n")));

        DBGMSG(1, (TEXT("Setting Bus Rate...\r\n")));
        XllpMMCSetBusRate(pContext, XLLP_MMC_304KHz, XLLP_MMC_CONTROLLER_1);

        DBGMSG(1, (TEXT("Setting Response Timeout...\r\n")));
        XllpMMCSetRespTimeout(pContext, 0x7f, XLLP_MMC_CONTROLLER_1);

        DBGMSG(1, (TEXT("Setting Read Timeout...\r\n")));
        XllpMMCSetReadTimeout(pContext, 0xffff, XLLP_MMC_CONTROLLER_1);

        DBGMSG(1, (TEXT("Masking all Interrupts...\r\n")));
        SDMMCMaskInterrupts(pContext);

        DBGMSG(1, (TEXT("Configuring...\r\n")));
        status = XllpMMCConfigure(0, pContext);

        if (status != XLLP_STATUS_SUCCESS)
        {
            DBGMSG(1, (TEXT("ERROR: %d - While initializing the hardware!\r\n"), status));
            return -1;
        }

        if (SDMMCIsCardInserted(pContext))
            return SDMMCCardInit(pContext);

        else
        {
            XllpMMCStopBusClock(pContext, XLLP_MMC_CONTROLLER_1);
            DBGMSG(1, (TEXT("\r\nNo card is detected... Trying again...\r\n\r\n")));
        }
    }
    DBGMSG(1, (TEXT("ERROR No card is inserted!\r\n")));

    return -1;
}


/****************************************************************
*    SDMMCMaskInterrupts()
*       Disables all interrupts because they are not needed in
*       eboot.
*    Input:
*       pContext - pointer to the context structure
*    Output:
*       all the interrupts are masked
*    Returns:
*       none
*****************************************************************/
void SDMMCMaskInterrupts(P_XLLP_MMC_CONTEXT_T pContext)
{
    pContext->pMMC1Reg->mmc_i_mask = (XLLP_MMC_I_MASK_DATA_TRAN_DONE | 
                                    XLLP_MMC_I_MASK_PRG_DONE |
                                    XLLP_MMC_I_MASK_END_CMD_RES |
                                    XLLP_MMC_I_MASK_STOP_CMD |
                                    XLLP_MMC_I_MASK_CLK_OFF |
                                    XLLP_MMC_I_MASK_RXFIFO_RD_REQ |
                                    XLLP_MMC_I_MASK_TXFIFO_WR_REQ |
                                    XLLP_MMC_I_MASK_TINT |
                                    XLLP_MMC_I_MASK_DAT_ERR |
                                    XLLP_MMC_I_MASK_RES_ERR |
                                    XLLP_MMC_I_MASK_SDIO_RD_STALLED |
                                    XLLP_MMC_I_MASK_SDIO_INT |
                                    XLLP_MMC_I_MASK_SDIO_SUSPEND_ACK);
}

/**********************************************************
*   SDMMCCardInit
*      Initializes the inserted card
*   Input:
*      pContext - pointer to the current context
*   Output:
*      none
*   Returns:
*      the RCA of the card
***********************************************************/
XLLP_UINT32_T SDMMCCardInit(P_XLLP_MMC_CONTEXT_T pContext)
{
    XLLP_UINT32_T argument, ocr, serialNum, rca, cardStatus, resp, cmd55_resp;
    XLLP_UINT32_T flags = NO_FLAGS;
    int i;
    int SD = FALSE;

    DBGMSG(1, (TEXT("Enabling Card Detect...\r\n")));
    XllpMMCEnableCardDetect(pContext, XLLP_MMC_SLOT_0);
    DBGMSG(1, (TEXT("Selecting MMC Slot 0...\r\n")));
    XllpMMCSelectSlot(pContext, XLLP_MMC_SLOT_0);

    DBGMSG(1, (TEXT("\r\nStarting Bus Clock...\r\n")));
    XllpMMCStartBusClock(pContext, XLLP_MMC_CONTROLLER_1);

    //Send CMD0 a few times to make sure card is in idle state
    for (i = 0; i < 3; i++)
    {
        argument = NO_ARGUMENT;
        flags |= XLLP_MMC_INIT_COMMAND;
        flags |= XLLP_MMC_RESPONSE_NONE;
        SDMMCSendCommand(pContext, XLLP_MMC_CMD0, argument, flags);
    }

    //send CMD1 until card is done initializing
    do
    {
        flags = NO_FLAGS;
        flags |= XLLP_MMC_INIT_COMMAND;
        flags |= XLLP_MMC_RESPONSE_R3;
        argument = OCR;

        SDMMCSendCommand(pContext, XLLP_MMC_CMD1, argument, flags);

        resp = SDMMCGetResponse(pContext, XLLP_MMC_RESPONSE_R3, TRUE);

        if (resp == SD_NO_CMD1 || resp == 0)
        {
            SD = TRUE;
            break;
        }
    }while (resp != MMC_BUSY_BIT);

    //if it's an SD card
    if (SD)
    {
        DBGMSG(1, (TEXT("No response so it must be a SD card!\r\n")));
        do
        {
            flags = NO_FLAGS;
            flags |= XLLP_MMC_INIT_COMMAND;
            flags |= XLLP_MMC_RESPONSE_R1;
            argument = LOW_ARG;

            SDMMCSendCommand(pContext, XLLP_MMC_CMD55, argument, flags);

            cmd55_resp = SDMMCGetResponse(pContext, XLLP_MMC_RESPONSE_R1, TRUE);

            flags = NO_FLAGS;
            flags |= XLLP_MMC_INIT_COMMAND;
            flags |= XLLP_MMC_RESPONSE_R3;
            argument = SD_OCR;

            SDMMCSendCommand(pContext, XLLP_SD_ACMD41, argument, flags);

            resp = SDMMCGetResponse(pContext, XLLP_MMC_RESPONSE_R3, TRUE);
        }while (resp != MMC_BUSY_BIT);
    }

    ocr = SDMMCGetResponse(pContext, XLLP_MMC_RESPONSE_R3, FALSE);

    //send CMD2 to get the CID numbers
    flags = NO_FLAGS;
    flags |= XLLP_MMC_NORMAL_COMMAND;
    flags |= XLLP_MMC_RESPONSE_R2;
    argument = NO_ARGUMENT;

    SDMMCSendCommand(pContext, XLLP_MMC_CMD2, argument, flags);

    serialNum = SDMMCGetResponse(pContext, XLLP_MMC_RESPONSE_R2, FALSE);

    if (SD)
    {
        //send CMD3 to assign a RCA to the card
        flags = NO_FLAGS;
        flags |= XLLP_MMC_NORMAL_COMMAND;
        flags |= XLLP_MMC_RESPONSE_R6;
        argument = NO_ARGUMENT;

        SDMMCSendCommand(pContext, XLLP_MMC_CMD3, argument, flags);

        rca = SDMMCGetResponse(pContext, XLLP_MMC_RESPONSE_R6, FALSE);
    }

    else
    {
        //send CMD3 to assign a RCA to the card
        flags = NO_FLAGS;
        flags |= XLLP_MMC_NORMAL_COMMAND;
        flags |= XLLP_MMC_RESPONSE_R1;
        rca = (serialNum >> 8) + 1;
        argument = (rca << 16) | LOW_ARG;

        SDMMCSendCommand(pContext, XLLP_MMC_CMD3, argument, flags);

        cardStatus = SDMMCGetResponse(pContext, XLLP_MMC_RESPONSE_R1, FALSE);
    }

    //data transfer is ready to begin

    //send CMD13 to check the status of the card
    SDMMCGetCardStatus(pContext, rca);

    //send CMD7 to get card into transfer state
    flags = NO_FLAGS;
    flags |= XLLP_MMC_NORMAL_COMMAND;
    flags |= XLLP_MMC_RESPONSE_R1;
    argument = (rca << 16) | LOW_ARG;

    SDMMCSendCommand(pContext, XLLP_MMC_CMD7, argument, flags);

    cardStatus = SDMMCGetResponse(pContext, XLLP_MMC_RESPONSE_R1, FALSE);

    //send CMD13 to check the status of the card
    SDMMCGetCardStatus(pContext, rca);

    DBGMSG(1, (TEXT("Setting Bus Rate...\r\n")));
    XllpMMCSetBusRate(pContext, XLLP_MMC_19_5MHz, XLLP_MMC_CONTROLLER_1);

    DBGMSG(1, (TEXT("DONE INITIALIZING THE CARD!\r\n")));

    return (rca << 16) | LOW_ARG;
}

/***************************************************************
*   SDMMCSendCommand
*      Sends the given command with the given argument and
*      flags to the card.
*   Input:
*      pContext - pointer to the current context
*      cmd - the command to be sent to the card
*      argument - the argument for the command
*      flags - the flags for the command
*   Output:
*      none
*   Returns:
*      none
*****************************************************************/
void SDMMCSendCommand(P_XLLP_MMC_CONTEXT_T pContext, XLLP_UINT32_T cmd, 
					  XLLP_UINT32_T argument, XLLP_UINT32_T flags)
{	
    XllpMMCSendCommand(pContext, cmd, argument, flags, BLK_CNT, 
        BLK_LEN, XLLP_MMC_SLOT_0);
    msWait(1);
}

/***************************************************************
*   SDMMCGetResponse
*      Reads the response fifo and returns the needed 
*      information for each response type.
*   Input:
*      pContext - pointer to the current context
*      RespType - the type of response
*      in_while - needed for CMD1 calls
*   Output:
*      none
*   Returns:
*      R1 - the OCR
*      R2 - the serial number
*      R3 - the card status
****************************************************************/
XLLP_UINT32_T SDMMCGetResponse(P_XLLP_MMC_CONTEXT_T pContext, 
							   XLLP_UINT32_T RespType, BOOL in_while)
{
    XLLP_UINT32_T cardStatus, ocr, serialNum, manufacturerID, rca;
    XLLP_UINT32_T response[RESPONSE_LENGTH];
    int i;

    for (i=0; i<RESPONSE_LENGTH; i++)
        response[i] = pContext->pMMC1Reg->mmc_res;

    if (RespType == XLLP_MMC_RESPONSE_R1)
    {
        response[0] &= OCR_RESERVED;
        cardStatus = response[0] << 24;
        response[1] = response[1] << 8;
        cardStatus |= response[1];
        response[2] = response[2] >> 8;
        cardStatus |= response[2];

        return cardStatus;
    }

    else if (RespType == XLLP_MMC_RESPONSE_R2)
    {
        response[0] &= OCR_RESERVED;
        manufacturerID = response[0] << 16;
        manufacturerID |= response[1];

        serialNum = response[5] << 8;
        response[6] &= UPPER_BITS;
        response[6] = response[6] >> 8;
        serialNum |= response[6];

        return serialNum;
    }

    else if (RespType == XLLP_MMC_RESPONSE_R3)
    {
        if (in_while == FALSE)
        {
            response[0] &= OCR_RESERVED;
            response[0] = response[0] << 24;
            ocr = response[0];
            response[1] = response[1] << 8;
            ocr |= response[1];
            return ocr;
        }
        return response[0];
    }
    else if (RespType == XLLP_MMC_RESPONSE_R6)
    {
        response[0] &= OCR_RESERVED;
        rca = response[0] << 24;
        response[1] = response[1] << 8;
        rca |= response[1];
        rca = rca >> 16;
        return rca;
    }
    return 0;
}

/***********************************************************
*   SDMMCReadFifo
*      Reads the contents of the read fifo
*   Input:
*      pContext - pointer to the current context
*      buffer - the buffer where the contents of the fifo
*               will be stored.
*   Output:
*      buffer will contain the contents of the read fifo
*   Returns:
*      none
*************************************************************/
void SDMMCReadFifo(P_XLLP_MMC_CONTEXT_T pContext, BYTE *buffer)
{
    int i = 0;
    volatile BYTE *pMMC_RX_Fifo = 
        (volatile BYTE *)&(pContext->pMMC1Reg->mmc_rxfifo);
    do{
        *buffer = *pMMC_RX_Fifo;
        buffer++;
        i++;
    }while(i < 512);
}

/***********************************************************
*   SDMMCPrintFifo
*      Prints the contents of the read fifo
*   Input:
*      buffer - the buffer where the contents of the fifo
*               is stored.
*   Output:
*      none
*   Returns:
*      none
*************************************************************/
void SDMMCPrintFifo(BYTE *buffer)
{
    int i;
    for(i=0; i<BLK_LEN; i++)
        DBGMSG(1, (TEXT("%c"), buffer[i]));
}

/**********************************************************
*    isCardInserted()
*       Checks to see if there is a SD/MMC card inserted
*       into the SDMMC slot on the platform.
*    input:
*       none
*    output:
*       none
*    returns:
*       TRUE if a card is detected
*       FALSE if no card is detected
***********************************************************/
BOOL SDMMCIsCardInserted(P_XLLP_MMC_CONTEXT_T pContext)
{
    XllpMMCPowerOnSlot(pContext, XLLP_MMC_SLOT_0);
    XllpMMCStartBusClock(pContext, XLLP_MMC_CONTROLLER_1);

    if (XLLP_TRUE == XllpMMCIsCardInserted(pContext, XLLP_MMC_SLOT_0))
        return TRUE;
    else
        return FALSE;
}

/***********************************************************
*    SDMMCReadBlock()
*       Reads the given block off of the SD/MMC card and 
*       into the tmp buffer.
*    input:
*       pContext - pointer to the context
*       block - the number of the desired block
*       buffer - the buffer where the data from the block will 
*                be stored.
*    output:
*       buffer will contain the contents of the block
*    returns:
*       none
************************************************************/
void SDMMCReadBlock(P_XLLP_MMC_CONTEXT_T pContext, 
					XLLP_UINT32_T block, BYTE *buffer)
{
    XLLP_UINT32_T flags;

    //send CMD17 to read a single block off of the card
    flags = NO_FLAGS;
    flags |= XLLP_MMC_NORMAL_COMMAND;
    flags |= XLLP_MMC_RESPONSE_R1;
    flags |= XLLP_MMC_DATA_ENABLE;

    SDMMCSendCommand(pContext, XLLP_MMC_CMD17, block, flags);

    SDMMCReadFifo(pContext, buffer);
}

/****************************************************************
*   SDMMCSetBlockLength
*      Sets the block length for the any read command called 
*      after this function.
*   Input:
*      pContext - pointer to the current context
*      length - the desired block length (usually 512)
*   Output:
*      none
*   Returns:
*      none
*****************************************************************/
void SDMMCSetBlockLength(P_XLLP_MMC_CONTEXT_T pContext, XLLP_UINT32_T length)
{
    XLLP_UINT32_T flags;

    //send CMD16 to set the block length for the read
    flags = NO_FLAGS;
    flags |= XLLP_MMC_NORMAL_COMMAND;
    flags |= XLLP_MMC_RESPONSE_R1;

    SDMMCSendCommand(pContext, XLLP_MMC_CMD16, length, flags);
}

/****************************************************************
*   SDMMCGetCardStatus
*      Gets the status of the card and prints the card state.
*   Input:
*      pContext - pointer to the current context
*      rca - the relative card address assigned to the card
*   Output:
*      none
*   Returns:
*      none
*****************************************************************/
void SDMMCGetCardStatus(P_XLLP_MMC_CONTEXT_T pContext, XLLP_UINT32_T rca)
{

    XLLP_UINT32_T flags, argument, cardStatus;

    //send CMD13 to check the status of the card
    flags = NO_FLAGS;
    flags |= XLLP_MMC_NORMAL_COMMAND;
    flags |= XLLP_MMC_RESPONSE_R1;

    argument = rca;

    SDMMCSendCommand(pContext, XLLP_MMC_CMD13, argument, flags);

    cardStatus = SDMMCGetResponse(pContext, XLLP_MMC_RESPONSE_R1, FALSE);
}

static void Wait(UINT32 microSeconds)
{
    volatile UINT32 *TimerOSCRAddress = (volatile UINT32 *)
    OALPAtoVA((MONAHANS_BASE_REG_PA_OST + OSCR_OFFSET), FALSE);
    UINT32 Value, Time;

    Time = *TimerOSCRAddress;
    Value = Time + (microSeconds * 4);
    if (Value < Time)
        while (Time < *TimerOSCRAddress);
    while (*TimerOSCRAddress <= Value);
}


static void msWait(UINT32 msVal)
{
    Wait(msVal * 1000);
}
